﻿/*
 * MapyCzFeatureSet
 * 
 * initial implementation : 18.7.2011
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections;
using System.Collections.Generic;
using Petra.Gis.DataServices;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;

namespace Petra.Gis.DataServices.MapyCz
{
  public abstract partial class MapyCzFeatureSet : FeatureSetBase
  {

    private const int pow_2_5 = 32; //(int)Math.Pow(2, 5); //=32
    private const int MAX_NUM = 268435455;
    private const int MASK_NUM = 268435456;
    private const int TILE_SIZE = 256;
    private const string SZN_SERVER_URL = "http://m2.mapserver.mapy.cz/";
    protected XParams aktivniNastaveni;

    /// <summary>
    /// prevod z UTM33N na souradnice dlazdicoveho systemu seznamu nazvaneho PP
    /// </summary>
    /// <param name="?"></param>
    /// <returns></returns>
    private BoundingBox utm33N2pp(BoundingBox UTM33EEBbox)
    {
      Coordinate coordMin = new Coordinate(UTM33EEBbox.Left, UTM33EEBbox.Bottom);
      Coordinate coordMax = new Coordinate(UTM33EEBbox.Right, UTM33EEBbox.Top);

      return new BoundingBox(utm33N2pp(coordMin).X, utm33N2pp(coordMin).Y, utm33N2pp(coordMax).X, utm33N2pp(coordMax).Y);

    }






    /// <summary>
    /// prevod z dlazdicoveho systemu seznamu nazvaneho PP na souradnice UTM33N
    /// </summary>
    /// <param name="PPBbox"></param>
    /// <returns></returns>
    private BoundingBox pp2utm33N(BoundingBox PPBbox)
    {
      Coordinate coordMin = new Coordinate(PPBbox.Left, PPBbox.Bottom);
      Coordinate coordMax = new Coordinate(PPBbox.Right, PPBbox.Top);

      return new BoundingBox(pp2utm33N(coordMin).X, pp2utm33N(coordMin).Y, pp2utm33N(coordMax).X, pp2utm33N(coordMax).Y);

    }







    /// <summary>
    /// prevod z UTM33EE na souradnice dlazdicoveho systemu seznamu nazvaneho PP
    /// </summary>
    /// <param name="UTMCoord"></param>
    /// <returns></returns>
    private Coordinate utm33N2pp(Coordinate UTMCoord)
    {

      Coordinate tmpCoord = new Coordinate();

      tmpCoord.X = (Math.Round(UTMCoord.X) - (-3700000)) * pow_2_5;
      tmpCoord.Y = (Math.Round(UTMCoord.Y) - (1300000)) * pow_2_5;

      return tmpCoord;

    }






    /// <summary>
    /// prevod z dlazdicoveho systemu seznamu nazvaneho PP na souradnice UTM33EE
    /// </summary>
    /// <param name="PPCoord"></param>
    /// <returns></returns>
    private Coordinate pp2utm33N(Coordinate PPCoord)
    {
      Coordinate tmpCoord = new Coordinate();

      tmpCoord.X = (PPCoord.X) / pow_2_5 + (-3700000);
      tmpCoord.Y = (PPCoord.Y) / pow_2_5 + 1300000;

      return tmpCoord;

    }





    /// <summary>
    /// prevod souradnic sour. systemu seznamu (PP) na textovou reprezentaci.
    /// Tato se pouzije pro sestaveni odkazu na obrazek dlazdice
    /// </summary>
    /// <param name="PPCoord"></param>
    /// <returns></returns>
    private string ppCoord2HEX(Coordinate PPCoord)
    {
      if (PPCoord.X > MAX_NUM || PPCoord.Y > MAX_NUM)
        throw new Exception("PPCoord2HEX:number is too big!");


      //pridani masky zajisti stale stejnou delku cisla ;-)
      return (MASK_NUM | (int)PPCoord.X).ToString("X").Substring(1)
      + "_" + (MASK_NUM | (int)PPCoord.Y).ToString("X").Substring(1);

    }




    protected void obnovDlazdiceProMapovyVyrez()
    {
      double utmTileSize = 0;
      int ppTileSize = 0;
      int scale;

      //updateTransform();

      //int canvasPixelWidth = (int)this.Width;
      FeatureSpatialFilter sf = (FeatureSpatialFilter)Parameters["Petra.Gis.DataServices.FeatureSpatialFilter"].Value;
      BoundingBox vyrezUTM33EE = sf.Bounds;

      //ulozit sirku a vysku mapoveho vyrezu v UTM33EE souradnicich
      double utmMapWidth = vyrezUTM33EE.Width;
      double utmMapHeight = vyrezUTM33EE.Height;

      //zjistit, ktere meritko dlazdice pouzit
      //staci pracovat se souradnicemi ve smeru X
      //dlazdice jsou 256x256px = oba smery stejna velikost

      double pixPerUTMTile = 0;

      //double pixPerUTMMap = canvasPixelWidth / utmMapWidth;
      double pixPerUTMMap = sf.GridResolution;

      Coordinate tmp1 = new Coordinate();
      Coordinate tmp2 = new Coordinate();

      tmp2.X = 0;
      tmp2.Y = 0;
      tmp2 = pp2utm33N(tmp2);

      //pres vsechna meritka proved test vhodnosti
      //stupne zvetseni mapy
      for (scale = 3; scale <= 16; scale++)
      {
        tmp1.X = Math.Pow(2, (28 - scale));
        tmp1.Y = 0;
        //vypocet velikosti (sirky i vysky) dlazdice v PP
        ppTileSize = (int)tmp1.X;
        //prevod souradnice z PP na UTM33EE
        tmp1 = pp2utm33N(tmp1);
        //vypocet velikosti dlazdice v UTM33EE
        utmTileSize = tmp1.X - tmp2.X;
        //vypocet pomeru pro meritko
        pixPerUTMTile = TILE_SIZE / (tmp1.X - tmp2.X);

        //jestli ze pocet pixelu na jednotku UTM souradnice mapy
        //je mensi nebo roven pocetu pixelu dlazdic na jeji UTM velikost
        if (pixPerUTMTile >= pixPerUTMMap)
          break;


      }

      //ulozeni aktivniho nastaveni pro pripad posuvu mapy
      aktivniNastaveni.scale = scale;
      aktivniNastaveni.ppTileSize = ppTileSize;
      aktivniNastaveni.UTMTileSize = utmTileSize;

      ObnovMapuPriPosuvu(vyrezUTM33EE);

    }




    /// <summary>
    /// pokud se mapa pouze posouva (nemeni se meritko ani rozmer mapy
    /// zavolani teto funkce donactou dlazdice ktere by chybely v novem vyrezu
    /// proti predchozimu
    /// </summary>
    /// <param name="novyVyrezUTM33EE">novy vyrez pro ktery se bude zjistovat seznam chybejicich...</param>
    public void ObnovMapuPriPosuvu(BoundingBox novyVyrezUTM33EE)
    {

      //prevod vyrezu mapy z UTM33EE do PP souradnic
      BoundingBox tmpBbox = utm33N2pp(novyVyrezUTM33EE);

      //zjisti pocatecni souradnici leveho dolniho rohu, leve dolni dlazdice v PP
      Coordinate outPP = new Coordinate(tmpBbox.Left, tmpBbox.Bottom);

      //prostym bitovym posuvem se ziska souradnice pocatku
      //dlazdice od ktere zacina zobrazitelna mapa
      outPP.X = (((long)outPP.X) >> (28 - aktivniNastaveni.scale)) << (28 - aktivniNastaveni.scale);
      outPP.Y = (((long)outPP.Y) >> (28 - aktivniNastaveni.scale)) << (28 - aktivniNastaveni.scale);

      //preved na bbox v PP
      BoundingBox outTilePP = new BoundingBox(outPP.X, outPP.Y, outPP.X + aktivniNastaveni.ppTileSize, outPP.Y + aktivniNastaveni.ppTileSize);
      //preved je jeste zpet na UTM33EE
      BoundingBox outTileUTM33EE = pp2utm33N(outTilePP);

      //zahajeni vlastniho naplneni seznamu dlazdic
      //double startDx = outTileUTM33EE.Right - vyrezUTM33EE.Left;
      double tileUTM33EEStartX = outTileUTM33EE.Left;
      double outPPStartX = outPP.X;
      double dx;
      double dy = outTileUTM33EE.Bottom - novyVyrezUTM33EE.Bottom;

      //Dlazdice tmpDlazdice;
      Grid tmpDlazdice;
      string kratkeJmeno;
      Uri dlazdiceUri;

      //cyklus v ose Y
      while (dy <= novyVyrezUTM33EE.Height)
      {
        //reset dx
        dx = tileUTM33EEStartX - novyVyrezUTM33EE.Left;
        //cyklus v ose X
        while (dx <= novyVyrezUTM33EE.Width)
        {
          //tmpDlazdice = Dlazdice.Create(this.Format);
          tmpDlazdice = new Grid();

          //transformovat zatim bounds zde...
          //tmpDlazdice.Bounds = aktivniNastaveni.DisplayTransform.ProjectionToMap(outTileUTM33EE);

          //kratke jmeno je jmeno bez http://mapy.....
          //jen 13_8050000_7e70000
          kratkeJmeno = aktivniNastaveni.scale.ToString() + "_" + ppCoord2HEX(outPP);
          dlazdiceUri = new Uri(SZN_SERVER_URL + this.kodMapy + "/" + kratkeJmeno);

          //tmpDlazdice = new Grid(GridData.Create(dlazdiceUri, new BoundingBox(bounds[6], bounds[7], bounds[0], bounds[1])));
          tmpDlazdice = new Grid(GridData.Create(dlazdiceUri, outTileUTM33EE));

          if (!_data.ContainsKey(kratkeJmeno))
          {
            //ulozit dlazdice do prohledavatelne struktury
            _data.Add(kratkeJmeno, tmpDlazdice);
            //ulozit dlazdice pro zobrazeni sablonou
            //sd.Add(tmpDlazdice);
          }
          //upravit hodnoty
          //dx
          dx += aktivniNastaveni.UTMTileSize;
          outTileUTM33EE = new BoundingBox(outTileUTM33EE.Left + aktivniNastaveni.UTMTileSize, outTileUTM33EE.Bottom, outTileUTM33EE.Right + aktivniNastaveni.UTMTileSize, outTileUTM33EE.Top);
          outPP = new Coordinate(outPP.X + aktivniNastaveni.ppTileSize, outPP.Y);
        }
        //dy
        dy += aktivniNastaveni.UTMTileSize;
        outTileUTM33EE = new BoundingBox(tileUTM33EEStartX, outTileUTM33EE.Bottom + aktivniNastaveni.UTMTileSize, tileUTM33EEStartX + aktivniNastaveni.UTMTileSize, outTileUTM33EE.Top + aktivniNastaveni.UTMTileSize);
        outPP = new Coordinate(outPPStartX, outPP.Y + aktivniNastaveni.ppTileSize);
      }

    }


  }
}
